/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simplemetrosystem.control;

import java.awt.Color;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
import simplemetrosystem.control.Exception.ElementFactoryException;

/**
 * 元素工厂类，具有单例属性，只能有其自身的静态方法得到实例。
 * 解析地图文件，生成元素管理器及撤销管理器
 * 地图
 * 
 * @author Eric Qian
 */
public class ElementFactory {

    /**
     * 得到元素工厂的实例
     * 警告:若之前未执行初始化方法则会得到NULL
     * @return 元素工厂实例,可能为NULL
     */
    public static ElementFactory getElementFactory() {
        return elementFactory;
    }

    /**
     * 得到元素工厂新的实例
     * @return 元素工厂实例
     */
    public static ElementFactory newInstance() {
        elementFactory = new ElementFactory();
        return elementFactory;
    }

    /**
     * 得到元素工厂实例,若解析文件时发生错误,则抛出相应异常
     * @param address 文件所在地址
     * @return 元素工厂实例
     * @throws javax.xml.parsers.ParserConfigurationException
     * @throws org.xml.sax.SAXException
     * @throws java.io.IOException
     * @throws simplemetrosystem.control.Exception.ElementFactoryException
     */
    public static ElementFactory newInstance(String address)
            throws ParserConfigurationException, SAXException, IOException, ElementFactoryException {
        elementFactory = new ElementFactory(address);
        return elementFactory;
    }

    /*
     * 初始化元素工厂，元素管理器，撤销管理器
     */
    private ElementFactory() {
        elementManager = ElementManager.newInstance(null);
        editCommandManager = EditCommandManager.newInstance(20);
    }

    /**
     * 初始化元素工厂方法
     * @param address 地图文件地址
     * @throws javax.xml.parsers.ParserConfigurationException
     * @throws org.xml.sax.SAXException
     * @throws java.io.IOException
     * @throws simplemetrosystem.control.Exception.ElementFactoryException
     */
    public ElementFactory(String address)
            throws ParserConfigurationException, SAXException, IOException, ElementFactoryException {
        elementManager = ElementManager.newInstance(address);
        editCommandManager = EditCommandManager.newInstance(20);

        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = db.parse(address);
        Element root = doc.getDocumentElement();

        parseChild(root);
    }

    /*
     * 解析根节点，抛出异常
     */
    private void parseChild(Element root) throws ElementFactoryException {

        NodeList children = root.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if (child instanceof Element) {
                Element childElement = (Element) child;
                if (childElement.getTagName().equals(ROOT_WIDTH)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    elementManager.setWidth(Integer.parseInt(textNode.getData().trim()));
                } else if (childElement.getTagName().equals(ROOT_HEIGHT)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    elementManager.setHeight(Integer.parseInt(textNode.getData().trim()));
                } else if (childElement.getTagName().equals(ROOT_LINE)) {
                    newLine(childElement);
                } else if (childElement.getTagName().equals(ROOT_STATION)) {
                    newStation(childElement);
                } else if (childElement.getTagName().equals(ROOT_EDGE)) {
                    newEdge(childElement);
                } else if (childElement.getTagName().equals(ROOT_STRING)) {
                    newString(childElement);
                } else {
                    throw new ElementFactoryException("XML ERROR:ROOT TAG");
                }
            }
        }
    }

    /*
     * 解析线路标签，抛出异常
     */
    private void newLine(Element line) throws ElementFactoryException {
        NodeList lineNode = line.getChildNodes();
        String lineName = null;
        String lineCName = null;
        String status = null;
        Color color = null;
        int red = -1;
        int green = -1;
        int blue = -1;

        for (int i = 0; i < lineNode.getLength(); i++) {
            Node child = lineNode.item(i);

            if (child instanceof Element) {
                Element childElement = (Element) child;
                if (childElement.getTagName().equals(LINE_NAM)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    lineName = textNode.getData().trim();
                } else if (childElement.getTagName().equals(LINE_CNAM)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    lineCName = textNode.getData().trim();
                } else if (childElement.getTagName().equals(LINE_STATUS)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    status = textNode.getData().trim();
                } else if (childElement.getTagName().equals(LINE_COLOR)) {
                    NodeList colorList = childElement.getChildNodes();

                    for (int j = 0; j < colorList.getLength(); j++) {
                        Node colorNode = colorList.item(j);
                        if (colorNode instanceof Element) {
                            Element colorElement = (Element) colorNode;
                            if (colorElement.getTagName().equals(COLOR_RED)) {
                                Text textColorNode = (Text) colorElement.getFirstChild();
                                red = Integer.parseInt(textColorNode.getData().trim());
                            } else if (colorElement.getTagName().equals(COLOR_GREEN)) {
                                Text textColorNode = (Text) colorElement.getFirstChild();
                                green = Integer.parseInt(textColorNode.getData().trim());
                            } else if (colorElement.getTagName().equals(COLOR_BLUE)) {
                                Text textColorNode = (Text) colorElement.getFirstChild();
                                blue = Integer.parseInt(textColorNode.getData().trim());
                            } else {
                                throw new ElementFactoryException("XML ERROR:LINE TAG");
                            }
                        }
                    }
                    if (red >= 0 && red < 256 && green >= 0 && green < 256 && blue >= 0 && blue < 256) {
                        color = new Color(red, green, blue);
                    }
                } else {
                    throw new ElementFactoryException("XML ERROR:LINE TAG");
                }
            }
        }
        System.out.println(lineName + " " + lineCName + " " + color.toString());
        elementManager.addLine(lineName, lineCName, status, color);
    }

    private void newStation(Element station) throws ElementFactoryException {
        NodeList stationNode = station.getChildNodes();

        String stationName = null;
        String stationCName = null;
        int logX = -1;
        int logY = -1;
        double natX = -1;
        double natY = -1;

        for (int i = 0; i < stationNode.getLength(); i++) {
            Node child = stationNode.item(i);

            if (child instanceof Element) {
                Element childElement = (Element) child;

                if (childElement.getTagName().equals(STAT_NAM)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    stationName = textNode.getData().trim();
                } else if (childElement.getTagName().equals(STAT_CNAM)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    stationCName = textNode.getData().trim();
                } else if (childElement.getTagName().equals(STAT_LOGX)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    logX = Integer.parseInt(textNode.getData().trim());
                } else if (childElement.getTagName().equals(STAT_LOGY)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    logY = Integer.parseInt(textNode.getData().trim());
                } else if (childElement.getTagName().equals(STAT_NATX)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    natX = Double.parseDouble(textNode.getData().trim());
                } else if (childElement.getTagName().equals(STAT_NATY)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    natY = Double.parseDouble(textNode.getData().trim());
                } else {
                    throw new ElementFactoryException("XML ERROR:STATION TAG");
                }

            }
        }
        System.out.println(
                stationName + " " + stationCName + " " + logX + " " + logY + " " + natX + " " + natY);
        elementManager.addStation(stationName, stationCName, logX, logY, natX, natY);
    }

    private void newEdge(Element edge) throws ElementFactoryException {
        NodeList edgeNode = edge.getChildNodes();

        String stationA = null;
        String stationB = null;
        String line = null;
        int serialNumber = -1;
        double distance = -1;
        double time = -1;

        for (int i = 0; i < edgeNode.getLength(); i++) {
            Node child = edgeNode.item(i);

            if (child instanceof Element) {
                Element childElement = (Element) child;

                if (childElement.getTagName().equals(EDG_STA)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    stationA = textNode.getData().trim();
                } else if (childElement.getTagName().equals(EDG_STB)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    stationB = textNode.getData().trim();
                } else if (childElement.getTagName().equals(EDG_LIN)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    line = textNode.getData().trim();
                } else if (childElement.getTagName().equals(EDG_SERNUM)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    serialNumber = Integer.parseInt(textNode.getData().trim());
                } else if (childElement.getTagName().equals(EDG_DIS)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    distance = Double.parseDouble(textNode.getData().trim());
                } else if (childElement.getTagName().equals(EDG_TIM)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    time = Double.parseDouble(textNode.getData().trim());
                } else {
                    Text textNode = (Text) childElement.getFirstChild();
                    String text = textNode.getData().trim();
                    System.out.print(childElement.getTagName() + " " + text);
                    throw new ElementFactoryException("XML ERROR:EDGE TAG");
                }
            }
        }
        System.out.println(stationA + " " + stationB + " " + line + " " + " " + distance + " " + time);
        elementManager.addEdge(stationA, stationB, line, serialNumber, distance, time);
    }

    private void newString(Element string) throws ElementFactoryException {
        NodeList stringNode = string.getChildNodes();

        String text = null;
        String element = null;
        int logX = 0;
        int logY = 0;

        for (int i = 0; i < stringNode.getLength(); i++) {
            Node child = stringNode.item(i);

            if (child instanceof Element) {
                Element childElement = (Element) child;

                if (childElement.getTagName().equals(STR_STR)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    text = textNode.getData().trim();
                } else if (childElement.getTagName().equals(STR_ELEM)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    element = textNode.getData().trim();
                } else if (childElement.getTagName().equals(STR_LOGX)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    logX = Integer.parseInt(textNode.getData().trim());
                } else if (childElement.getTagName().equals(STR_LOGY)) {
                    Text textNode = (Text) childElement.getFirstChild();
                    logY = Integer.parseInt(textNode.getData().trim());
                } else {
                    Text textNode = (Text) childElement.getFirstChild();
                    String textn = textNode.getData().trim();
                    System.out.print(childElement.getTagName() + " " + textn);
                    throw new ElementFactoryException("XML ERROR:EDGE TAG");
                }
            }
        }
        System.out.println(text + " " + element + " " + logX + " " + logY);
        elementManager.addString(text, element, logX, logY);
    }
    private static ElementFactory elementFactory;
    private ElementManager elementManager;
    private EditCommandManager editCommandManager;
    public static final String ROOT_WIDTH = "width";
    public static final String ROOT_HEIGHT = "height";
    /**
     * 根节点线路标签
     */
    public static final String ROOT_LINE = "line";
    /**
     * 根节点站点比阿标签
     */
    public static final String ROOT_STATION = "station";
    /**
     * 根节点关联标签
     */
    public static final String ROOT_EDGE = "edge";
    /**
     * 根节点字符串标签
     */
    public static final String ROOT_STRING = "string";
    /**
     * 线路节点英文标签
     */
    public static final String LINE_NAM = "name";
    /**
     * 线路节点中文标签
     */
    public static final String LINE_CNAM = "cName";
    /**
     * 线路节点状态标签
     */
    public static final String LINE_STATUS = "lineStatus";
    /**
     * 线路节点色彩标签
     */
    public static final String LINE_COLOR = "color";
    /**
     * 色彩节点红色标签
     */
    public static final String COLOR_RED = "red";
    /**
     * 色彩节点绿色标签
     */
    public static final String COLOR_GREEN = "green";
    /**
     * 色彩节点蓝色标签
     */
    public static final String COLOR_BLUE = "blue";
    /**
     * 站点节点英文名标签
     */
    public static final String STAT_NAM = "name";
    /**
     * 站点节点中文名标签
     */
    public static final String STAT_CNAM = "cName";
    /**
     * 站点节点逻辑横坐标标签
     */
    public static final String STAT_LOGX = "logX";
    /**
     * 站点节点逻辑纵坐标标签
     */
    public static final String STAT_LOGY = "logY";
    /**
     * 站点节点自然横坐标标签
     */
    public static final String STAT_NATX = "natX";
    /**
     * 站点节点自然纵坐标标签
     */
    public static final String STAT_NATY = "natY";
    /**
     * 关联节点站点A标签
     */
    public static final String EDG_STA = "stationA";
    /**
     * 关联节点站点B标签
     */
    public static final String EDG_STB = "stationB";
    /**
     * 关联节点所属线路标签
     */
    public static final String EDG_LIN = "line";
    /**
     * 关联节点序列号标签
     */
    public static final String EDG_SERNUM = "serialNumber";
    /**
     * 关联节点距离标签
     */
    public static final String EDG_DIS = "distance";
    /**
     * 关联节点时间标签
     */
    public static final String EDG_TIM = "time";
    /**
     * 文字节点元素标签
     */
    public static final String STR_ELEM = "element";
    /**
     * 文字节点文字标签
     */
    public static final String STR_STR = "string";
    /**
     * 文字节点横坐标标签
     */
    public static final String STR_LOGX = "logX";
    /**
     * 文字节点纵坐标标签
     */
    public static final String STR_LOGY = "logY";
}
